package org.ricks.schedule;

import org.ricks.common.ThreadNameFactory;
import org.ricks.log.Logger;

import java.util.Calendar;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author chenwei
 * @Description:
 * @date 2022/10/2716:20
 */
public class Schedule {

    private static int SECOND = 1000;
    private static long MINUTE = 60 * SECOND;
    private static long HOUR = 60 * MINUTE;
    private static long DAY  = 24 * HOUR;
    private static long WEEK = 7 * DAY;
    private String name = "thread";

    private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadNameFactory(1,name)); //Executors.newScheduledThreadPool(2);
    private Map taskMap = new ConcurrentHashMap<String, ScheduledFuture<?>>();

    public Schedule(String name) {
        this.name = name;
    }

    public ScheduledFuture scheduleOnce(long delay, TimeUnit unit, ITask task) {
        //schedule 方法有个问题，如果task一次都没有执行过，则无法停止，而scheduleWithFixedDelay则没有这个问题
        //ScheduledFuture<?> sf =scheduledExecutorService.schedule(new RunnableImpl(task,true), delay, unit);

        //相同taskId必须中断
        cancelTask(task.taskId());

        ScheduledFuture sf = scheduledExecutorService.scheduleWithFixedDelay(new RunnableImpl(task, true), delay, delay, unit);
        taskMap.put(task.taskId(), sf);
        return sf;
    }

    public ScheduledFuture startWithFixedDelay(long initialDelay,long delay,TimeUnit unit,ITask task) {
        //相同taskId必须中断
        cancelTask(task.taskId());

        ScheduledFuture sf = scheduledExecutorService.scheduleWithFixedDelay(new RunnableImpl(task, false), initialDelay, delay, unit);
        taskMap.put(task.taskId(), sf);
        return sf;
    }

    public ScheduledFuture startAtFixedRate(long initialDelay,long period,TimeUnit unit,ITask task) {
        //相同taskId必须中断
        cancelTask(task.taskId());

        ScheduledFuture sf = scheduledExecutorService.scheduleAtFixedRate(new RunnableImpl(task, false), initialDelay, period, unit);
        taskMap.put(task.taskId(), sf);
        return sf;
    }

    public ScheduledFuture scheduleOnceAtSpecifiedTime(Calendar cal,ITask task) {
        //相同taskId必须中断
        cancelTask(task.taskId());

        long initialDelay = getInitialDelay(cal);

        ScheduledFuture sf = scheduledExecutorService.scheduleWithFixedDelay(new RunnableImpl(task, true), initialDelay, initialDelay, TimeUnit.MILLISECONDS);
        taskMap.put(task.taskId(), sf);
        return sf;
        //scheduledExecutorService.schedule(new RunnableImpl(task,false), delay, TimeUnit.MILLISECONDS);
    }

    /**
     *
     * @param cal
     * @param period 天
     * @param task
     */
    public ScheduledFuture startWithFixedDelayAtSpecifiedTime(Calendar cal,long period,ITask task) {
        //相同taskId必须中断
        cancelTask(task.taskId());

        long initialDelay = getInitialDelay(cal);

        ScheduledFuture sf = scheduledExecutorService.scheduleWithFixedDelay(new RunnableImpl(task, false), initialDelay, period * Schedule.DAY, TimeUnit.MILLISECONDS);
        taskMap.put(task.taskId(), sf);
        return sf;
    }

    /**
     *
     * @param cal
     * @param period 天
     * @param task
     */
    public ScheduledFuture startAtFixedRateAtSpecifiedTime(Calendar cal,long period,ITask task) {
        //相同taskId必须中断
        cancelTask(task.taskId());

        long initialDelay = getInitialDelay(cal);
        //println(s"startAtFixedRateAtSpecifiedTime initialDelay:${initialDelay}")
        ScheduledFuture sf = scheduledExecutorService.scheduleAtFixedRate(new RunnableImpl(task, false), initialDelay, period * Schedule.DAY, TimeUnit.MILLISECONDS);
        taskMap.put(task.taskId(), sf);
        return sf;
    }

    private long getInitialDelay(Calendar cal) {
        long  today = System.currentTimeMillis();
        var initialDelay = cal.getTimeInMillis() - today;
        if (initialDelay < 0) {
            cal.set(Calendar.DAY_OF_YEAR, Calendar.getInstance().get(Calendar.DAY_OF_YEAR));
            cal.add(Calendar.DAY_OF_YEAR, 1);
            initialDelay = cal.getTimeInMillis() - today;
        }
        return initialDelay;
    }

    private void cancelTask(String taskId) {
        ScheduledFuture sf = (ScheduledFuture) taskMap.remove(taskId);
        if (sf != null) {
			/*if(!isExecuted){
				println(s"Schedule $taskId has existed ,the old be cancel, pls check it !!")
			}*/

            sf.cancel(true);
        }
    }

    public Map getTask() {
        return taskMap;
    }

    public boolean shutdown() {
        taskMap.values().forEach(obj -> {
            ScheduledFuture sf = (ScheduledFuture) obj;
            if (!sf.isDone() || !sf.isCancelled()) {
                sf.cancel(true);
            }
        });

        scheduledExecutorService.shutdown();
        while (!scheduledExecutorService.isTerminated()) try {
            Logger.warn("TaskExecutor is shutting down...");
            Thread.sleep(1000);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        return true;
    }

    private void shutdownNow() {
        scheduledExecutorService.shutdownNow();
    }

    /**
     * @author achou.lau
     */
    private class RunnableImpl implements Runnable {

        private ITask task;
        private boolean isRunOnce = false;

        public RunnableImpl(ITask task,boolean isRunOnce) {
            this.task = task;
            this.isRunOnce = isRunOnce;
        }

        @Override
        public void run() {
            try {
                task.doTask();
            } catch (Exception e){
                e.printStackTrace();
            } finally {
                if (isRunOnce) cancelTask(task.taskId());
            }
        }
    }

}
